//! 资源缓存
//!
//! 提供 LRU 缓存机制，用于缓存已加载的资源

use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

/// 资源缓存
///
/// 使用 LRU 策略缓存资源，自动淘汰最少使用的资源
///
/// # 类型参数
///
/// - `T`: 资源类型（如数据库连接、规则引擎等）
pub struct ResourceCache<T> {
    /// 缓存存储
    cache: Arc<RwLock<HashMap<String, CacheEntry<T>>>>,
    /// 最大缓存数量
    max_size: usize,
}

/// 缓存条目
struct CacheEntry<T> {
    /// 资源
    resource: Arc<T>,
    /// 访问次数
    access_count: usize,
    /// 最后访问时间
    last_access: std::time::Instant,
}

impl<T> ResourceCache<T> {
    /// 创建新的资源缓存
    ///
    /// # 参数
    ///
    /// - `max_size`: 最大缓存数量
    pub fn new(max_size: usize) -> Self {
        Self { cache: Arc::new(RwLock::new(HashMap::new())), max_size }
    }

    /// 获取缓存的资源
    pub async fn get(
        &self,
        key: &str,
    ) -> Option<Arc<T>> {
        let mut cache = self.cache.write().await;

        if let Some(entry) = cache.get_mut(key) {
            entry.access_count += 1;
            entry.last_access = std::time::Instant::now();
            Some(entry.resource.clone())
        } else {
            None
        }
    }

    /// 插入资源到缓存
    pub async fn insert(
        &self,
        key: String,
        resource: Arc<T>,
    ) {
        let mut cache = self.cache.write().await;

        // 如果缓存已满，移除最少使用的条目
        if cache.len() >= self.max_size {
            self.evict_lru(&mut cache);
        }

        cache.insert(key, CacheEntry { resource, access_count: 1, last_access: std::time::Instant::now() });
    }

    /// 从缓存中移除资源
    pub async fn remove(
        &self,
        key: &str,
    ) -> Option<Arc<T>> {
        let mut cache = self.cache.write().await;
        cache.remove(key).map(|entry| entry.resource)
    }

    /// 清空缓存
    pub async fn clear(&self) {
        let mut cache = self.cache.write().await;
        cache.clear();
    }

    /// 获取缓存统计信息
    ///
    /// 返回 (当前缓存数量, 最大缓存数量)
    pub async fn stats(&self) -> (usize, usize) {
        let cache = self.cache.read().await;
        (cache.len(), self.max_size)
    }

    /// 淘汰最少使用的条目
    fn evict_lru(
        &self,
        cache: &mut HashMap<String, CacheEntry<T>>,
    ) {
        if cache.is_empty() {
            return;
        }

        // 找到访问次数最少且最久未访问的条目
        let lru_key = cache.iter().min_by_key(|(_, entry)| (entry.access_count, std::cmp::Reverse(entry.last_access))).map(|(k, _)| k.clone());

        if let Some(key) = lru_key {
            cache.remove(&key);
        }
    }
}

impl<T> Clone for ResourceCache<T> {
    fn clone(&self) -> Self {
        Self { cache: self.cache.clone(), max_size: self.max_size }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_cache_basic() {
        let cache = ResourceCache::new(2);

        let resource1 = Arc::new("resource1");
        let resource2 = Arc::new("resource2");

        cache.insert("key1".to_string(), resource1.clone()).await;
        cache.insert("key2".to_string(), resource2.clone()).await;

        assert_eq!(cache.get("key1").await.as_deref(), Some(&"resource1"));
        assert_eq!(cache.get("key2").await.as_deref(), Some(&"resource2"));
    }

    #[tokio::test]
    async fn test_cache_eviction() {
        let cache = ResourceCache::new(2);

        cache.insert("key1".to_string(), Arc::new("resource1")).await;
        cache.insert("key2".to_string(), Arc::new("resource2")).await;

        // 访问 key1，增加其访问次数
        let _ = cache.get("key1").await;

        // 插入第三个资源，应该淘汰 key2（访问次数最少）
        cache.insert("key3".to_string(), Arc::new("resource3")).await;

        assert!(cache.get("key1").await.is_some());
        assert!(cache.get("key2").await.is_none());
        assert!(cache.get("key3").await.is_some());
    }

    #[tokio::test]
    async fn test_cache_stats() {
        let cache = ResourceCache::<String>::new(5);

        cache.insert("key1".to_string(), Arc::new("resource1".to_string())).await;
        cache.insert("key2".to_string(), Arc::new("resource2".to_string())).await;

        let (current, max) = cache.stats().await;
        assert_eq!(current, 2);
        assert_eq!(max, 5);
    }
}
